package com.mldong.modules.sys.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mldong.base.CommonPage;
import com.mldong.base.YesNoEnum;
import com.mldong.modules.sys.api.TaskExecutionApi;
import com.mldong.modules.sys.dto.ThirdPartyCallbackPageParam;
import com.mldong.modules.sys.dto.ThirdPartyCallbackParam;
import com.mldong.modules.sys.dto.ThirdPartyCallbackResult;
import com.mldong.modules.sys.enums.ThirdPartyCallbackHandleStatusEnum;
import com.mldong.modules.sys.handler.ThirdPartyCallbackHandler;
import com.mldong.modules.sys.handler.ThirdPartyCallbackPostHandler;
import com.mldong.modules.sys.tasks.ThirdPartyCallbackTaskRunner;
import com.mldong.modules.sys.vo.ThirdPartyCallbackVO;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;

import com.mldong.util.LowCodeServiceUtil;
import com.mldong.modules.sys.entity.ThirdPartyCallback;
import com.mldong.modules.sys.mapper.ThirdPartyCallbackMapper;
import com.mldong.modules.sys.service.ThirdPartyCallbackService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
 * <p>
 * 第三方回调处理 服务实现类
 * </p>
 *
 * @author mldong
 * @since 2025-10-12
 */
@Service
@RequiredArgsConstructor
public class ThirdPartyCallbackServiceImpl extends ServiceImpl<ThirdPartyCallbackMapper, ThirdPartyCallback> implements ThirdPartyCallbackService {
    private final TaskExecutionApi taskExecutionApi;
    private final Map<String,ThirdPartyCallbackHandler> thirdPartyCallbackHandlerMap;
    private final Map<String, ThirdPartyCallbackPostHandler> thirdPartyCallbackPostHandlerMap;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(ThirdPartyCallbackParam param) {
        param.setId(null);
        ThirdPartyCallback thirdPartyCallback = new ThirdPartyCallback();
        BeanUtil.copyProperties(param, thirdPartyCallback);
        return super.save(thirdPartyCallback);
    }

    @Override
    public boolean update(ThirdPartyCallbackParam param) {
        ThirdPartyCallback thirdPartyCallback = new ThirdPartyCallback();
        BeanUtil.copyProperties(param, thirdPartyCallback);
        return LowCodeServiceUtil.updateById(baseMapper,thirdPartyCallback,ThirdPartyCallbackParam.class);
    }

    @Override
    public CommonPage<ThirdPartyCallbackVO> page(ThirdPartyCallbackPageParam param) {
        IPage<ThirdPartyCallbackVO> page = param.buildMpPage();
        QueryWrapper queryWrapper = param.buildQueryWrapper();
        queryWrapper.eq("t.is_deleted",YesNoEnum.NO);
        List<ThirdPartyCallbackVO> list = baseMapper.selectCustom(page, queryWrapper);
        page.setRecords(list);
        return CommonPage.toPage(page);
    }
    @Override
    public ThirdPartyCallbackVO findById(Long id) {
        return baseMapper.findById(id);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public String handle(String body, String channelType, String serviceType) {
        // 回调数据入库
        ThirdPartyCallback thirdPartyCallback = new ThirdPartyCallback();
        thirdPartyCallback.setBody(body);
        thirdPartyCallback.setChannelType(channelType);
        thirdPartyCallback.setServiceType(serviceType);
        thirdPartyCallback.setHandleStatus(ThirdPartyCallbackHandleStatusEnum.UNTREATED.getCode());
        baseMapper.insert(thirdPartyCallback);
        // 添加任务
        taskExecutionApi.addTaskToQueue("第三方回调补偿任务", ThirdPartyCallbackTaskRunner.class,thirdPartyCallback.getId(), 5, Dict.of("bizData",thirdPartyCallback));
        // 设置状态为处理中===>相当于立即处理
        taskExecutionApi.updateTaskState(thirdPartyCallback.getId(), 1, null);
        return handle(thirdPartyCallback, true);
    }
    private final static String defaultResult = "FAIL";
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String handle(ThirdPartyCallback thirdPartyCallback, boolean isUpdateTask) {
        String serviceType = thirdPartyCallback.getServiceType();
        serviceType = StrUtil.toUnderlineCase(serviceType);
        serviceType = StrUtil.toCamelCase(serviceType);
        String handleName = StrUtil.toCamelCase(serviceType+"CallbackHandler");
        ThirdPartyCallbackHandler thirdCallbackHandler = thirdPartyCallbackHandlerMap.get(handleName);
        if(thirdCallbackHandler == null) return defaultResult;
        ThirdPartyCallbackResult handleResult = thirdCallbackHandler.handle(thirdPartyCallback.getBody());
        handleResult.setThirdPartyCallbackId(thirdPartyCallback.getId());
        ThirdPartyCallback up = new ThirdPartyCallback();
        up.setId(handleResult.getThirdPartyCallbackId());
        up.setServiceId(handleResult.getServiceId());
        up.setHandleResult(handleResult.getHandleResult());
        if(handleResult.isSuccess()) {
            if(isUpdateTask){
                // 处理成功，归档到历史表
                taskExecutionApi.archiveTaskToHistory(thirdPartyCallback.getId(), 2, new Date());
            }
            up.setHandleStatus(ThirdPartyCallbackHandleStatusEnum.SUCCESS.getCode());
        } else {
            if(isUpdateTask){
                // 处理失败，更新错误次数
                taskExecutionApi.updateTaskState(thirdPartyCallback.getId(), 9, handleResult.getHandleResult());
                taskExecutionApi.incrementErrorCount(thirdPartyCallback.getId());
            }
            up.setHandleStatus(ThirdPartyCallbackHandleStatusEnum.FAIL.getCode());
        }
        baseMapper.updateById(up);
        // 第三方回调处理完成处理类，由此去更新业务表状态
        thirdPartyCallbackPostHandlerMap.forEach((k, thirdPartyCallbackPostHandler)->{
            thirdPartyCallbackPostHandler.handle(handleResult);
        });
        if(isUpdateTask){
            return handleResult.getResult();
        }
        return handleResult.isSuccess() ? "SUCCESS" : "FAIL";
    }
}
